package login

import (
	"net/http"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"

	"cgs-server/helper"
	"cgs-server/server"
)

func getClientType(clientType string) string {
	switch clientType {
	case "":
	case "1":
		return "web"
	case "2":
		return "mobile"
	default:
		return "web"
	}
	return "web"
}

func init() {
	//server.Handle(http.MethodPost, "/api/Login/Login", Login, server.None)
	server.Post("/api/Login/Login", Login, "登录", server.None)
}

// Login log in the system.
func Login(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	username := strings.TrimSpace(r.FormValue("Username"))
	password := strings.TrimSpace(r.FormValue("Password"))
	// 0: web端，1：手机端
	clientType := strings.TrimSpace(r.FormValue("ClientType"))
	//uni push client id for push message
	clientId := strings.TrimSpace(r.FormValue("ClientId"))
	if clientType == "" {
		clientType = "0"
	}

	if clientType != "" && clientType != "0" && clientType != "1" {
		helper.WriteJSON(w, server.Result{
			Code: 300,
			Msg:  "not allowed client",
		})
		return
	}

	if username == "" {
		helper.WriteJSON(w, server.Result{
			Code: 300,
			Msg:  "Username is not allowed to be empty.",
		})
		return
	}

	if password == "" {
		helper.WriteJSON(w, server.Result{
			Code: 300,
			Msg:  "Password is not allowed to be empty.",
		})
		return
	}

	// get salt
	db, err := server.Mongo()
	if err != nil {
		helper.WriteJSON(w, server.Result{
			Code: 300,
			Msg:  err.Error(),
		})
		return
	}

	filter := bson.M{
		"Username": username,
		"$or": bson.A{
			bson.M{
				"isDeleted": 0,
			},
			bson.M{
				"isDeleted": nil,
			},
		},
	}

	user := bson.M{}
	find, _ := db.FindOne(server.UserCollectionName, filter, &user)
	if !find {
		helper.WriteJSON(w, server.Result{
			Code: 300,
			Msg:  "The username or password is wrong.",
		})
		return
	}

	salt := user["Salt"].(string)

	// verity password
	pwd1 := user["Password"].(string)
	pwd2 := helper.MD5(password + salt)
	if pwd1 != pwd2 {
		helper.WriteJSON(w, server.Result{
			Code: 300,
			Msg:  "The username or password is wrong.",
		})
		return
	}

	// write userID to cookie
	id := user["ID"].(primitive.ObjectID).Hex()

	token := jwt.New(jwt.SigningMethodHS256)
	claims := make(jwt.MapClaims)
	claims["exp"] = time.Now().Add(time.Hour * time.Duration(24*30)).Unix()
	claims["iat"] = time.Now().Unix()
	claims["userID"] = id
	token.Claims = claims

	tokenString, err := token.SignedString([]byte(server.Config.Authority.SecretKey))
	if err != nil {
		helper.WriteJSON(w, server.Result{
			Code: 300,
			Msg:  "Error while signing the token.",
		})
		return
	}

	expire := time.Now().AddDate(0, 0, 1)
	cookie := http.Cookie{
		Name:     "token",
		Value:    tokenString,
		Expires:  expire,
		HttpOnly: true,
		Path:     "/",
		SameSite: http.SameSiteLaxMode,
		//Secure: true,
	}
	http.SetCookie(w, &cookie)
	xff := r.Header.Get("X-Forwarded-For")
	//save and enable token
	tokenDoc := bson.M{}
	tokenDoc["UserId"] = id
	tokenDoc["CreateTime"] = time.Now().In(server.CstZone)
	tokenDoc["UpdateTime"] = time.Now().In(server.CstZone)
	tokenDoc["TokenValue"] = tokenString
	tokenDoc["Enabled"] = 1
	tokenDoc["Host"] = xff
	tokenDoc["ClientType"] = clientType
	tokenDoc["ClientId"] = clientId
	db.InsertOne(server.CgsTokenCollectionName, tokenDoc)
	//disable other token of this user
	tokenfilter := bson.M{
		"UserId": id,
		"TokenValue": bson.M{
			"$ne": tokenString,
		},
		"ClientType": clientType,
	}
	//set := bson.M{
	//	"UpdateTime": time.Now().In(server.CstZone),
	//	"Enabled":    0,
	//}
	//update := bson.M{
	//	"$set": set,
	//}
	db.DeleteMany(server.CgsTokenCollectionName, tokenfilter)

	helper.WriteJSON(w, server.Result{
		Code: 200,
		Msg:  "Login successfully!",
		Data: map[string]string{
			"Username": user["Username"].(string),
			"Name":     user["Name"].(string),
			"Token":    tokenString,
		},
	})

	server.WebsocketHub.SendByUser(&server.BroadcastMessage{
		Type: "token_status",
		Data: bson.M{
			"invalid":    true,
			"clientType": getClientType(clientType),
		},
	}, server.User{
		Id:    id,
		Token: tokenString,
	})
}
